<template>
    <view class="select-container" v-show="show" @touchmove.stop.prevent>
        <view class="mask" :class="activeClass ? 'mask-show' : ''" @tap="onCancel(true)"></view>
        <view class="select-box" :class="activeClass ? 'select-box-show' : ''">
            <view class="header">
                <text class="cancel" @tap="onCancel">{{cancelText}}</text>
                <view class="all" @tap="onAllToggle" v-if="allShow">
                    <text :class="isAll ? 'all-active' : ''">全选</text>
                </view>
                <text v-if="data.length" class="confirm" @tap="onConfirm">{{confirmText}}</text>
            </view>
            <view class="body-warp">
                <scroll-view class="body" scroll-y="true">
                    <view v-if="!data.length" name="tips">
                        <view class="empty-tips">暂无数据</view>
                    </view>
                    <view v-else class="select-item"
                        :class="[item.disabled ? 'disabled' : '',selectedArr[index] ? 'selected' : '']"
                        v-for="(item,index) in data" :key="item[valueName]" @tap="onSelected(index)">
                        <view class="label">{{item[labelName]}}</view>
                        <text v-show="selectedArr[index]" class="selected-icon">✔</text>
                    </view>
                </scroll-view>
            </view>
        </view>
    </view>
</template>
<!-- 多选组件 -->
<script>
    export default {
        model: {
            prop: "value",
            event: ["input"]
        },
        data() {
            return {
                show: false, //是否显示
                activeClass: false, //激活样式状态
                selectedArr: [], //选择对照列表
                selectedArrOld: [] //选择对照列表上一次的数据
            };
        },
        onShow() {
            this.show = this.value;
        },
        computed: {
            // 返回是否全选
            isAll() {
                let wipeDisabledList = this.returnWipeDisabledList();
                if (!wipeDisabledList.length) return false;
                return !wipeDisabledList.includes(false);
            }
        },
        props: {
            // 双向绑定
            value: {
                type: Boolean,
                default: false
            },
            // 取消按钮文字
            cancelText: {
                type: String,
                default: "取消"
            },
            // 确认按钮文字
            confirmText: {
                type: String,
                default: "确认"
            },
            // label对应的key名称
            labelName: {
                type: String,
                default: "label"
            },
            // value对应的key名称
            valueName: {
                type: String,
                default: "value"
            },
            // 是否允许点击遮罩层关闭
            maskCloseAble: {
                type: Boolean,
                default: true
            },
            // 是否显示全选
            allShow: {
                type: Boolean,
                default: true
            },
            // 模式
            mode: {
                type: String,
                default: "multiple"
            },
            // 默认选中值
            defaultSelected: {
                type: Array,
                default: function() {
                    return [];
                }
            },
            // 数据源
            data: {
                type: Array,
                required: true,
                default: () => {
                    return [];
                }
            }
        },
        watch: {
            async value(newVal) {
                this.show = newVal;
                await this.$nextTick();
                this.activeClass = newVal;
                if (newVal) {
                    this.selectedArrOld = JSON.parse(JSON.stringify(this.selectedArr));
                }
            },
            show(newVal) {
                this.$emit("input", newVal);
                this.$emit("change", newVal);
            },
            data: {
                // 设置初始选择对照列表
                handler(list) {
                    this.selectedArr = list.map(el => false);
                    this.setItemActiveState();
                },
                deep: true,
                immediate: true
            },
            defaultSelected: {
                handler() {
                    this.setItemActiveState();
                },
                deep: true,
                immediate: true
            }
        },
        methods: {
            // 设置默认选中通用办法
            setItemActiveState() {
                if (this.data.length && this.defaultSelected.length) {
                    this.data.forEach((item, i) => {
                        for (let n = 0; n < this.defaultSelected.length; n++) {
                            if (
                                !item.disabled &&
                                item[this.valueName] === this.defaultSelected[n]
                            ) {
                                this.selectedArr.splice(i, 1, true);
                                break;
                            }
                        }
                    });
                }
            },
            /**
             * 选择事件
             * @index {Number} 点击下标
             */
            onSelected(index) {
                if (this.data[index].disabled) return;
                if (this.mode === "multiple") {
                    let index2Active = this.selectedArr[index];
                    this.selectedArr.splice(index, 1, !index2Active);
                } else {
                    const newSelectedArr = this.selectedArr.map((item, idx) => idx === index);
                    this.selectedArr = newSelectedArr;
                }
            },
            // 取消事件
            onCancel(isMask) {
                if (!isMask || this.maskCloseAble) {
                    this.show = false;
                    this.selectedArr = JSON.parse(JSON.stringify(this.selectedArrOld));
                } else {
                    return;
                }
                this.$emit("cancel");
            },
            // 返回去除了disabled状态后的对照列表
            returnWipeDisabledList() {
                let arr = [];
                this.selectedArr.forEach((el, index) => {
                    if (!this.data[index].disabled) arr.push(el);
                });
                return arr;
            },
            // 全选/非全选事件
            onAllToggle() {
                let wipeDisabledList = this.returnWipeDisabledList();
                // 如果去除了disabled的对照列表有false的数据，代表未全选
                if (wipeDisabledList.includes(false)) {
                    this.selectedArr.forEach((el, index) => {
                        if (!this.data[index].disabled)
                            this.selectedArr.splice(index, 1, true);
                    });
                } else {
                    this.selectedArr.forEach((el, index) => {
                        if (!this.data[index].disabled)
                            el = this.selectedArr.splice(index, 1, false);
                    });
                }
            },
            // 确定事件
            onConfirm() {
                this.show = false;
                let selectedData = [];
                this.selectedArr.forEach((el, index) => {
                    if (el) {
                        selectedData.push(this.data[index]);
                    }
                });
                if (this.mode === "multiple") {
                    this.$emit("confirm", selectedData);
                } else {
                    let backData = selectedData[0] || {};
                    this.$emit("confirm", backData);
                }
            }
        }
    };
</script>
<style lang="scss" scoped>
    .select-container {
        width: 100vw;
        height: 100vh;
        position: fixed;
        left: 0;
        top: 0;
        z-index: 999;
        $paddingLR: 18rpx;

        .mask {
            width: 100%;
            height: 100%;
            background-color: $uni-bg-color-mask;
            opacity: 0;
            transition: opacity 0.3s;

            &.mask-show {
                opacity: 1;
            }
        }

        // 选择器内容区域
        .select-box {
            width: 100%;
            position: absolute;
            bottom: 0;
            left: 0;
            transform: translate3d(0px, 100%, 0px);
            background-color: $uni-bg-color;
            transition: all 1s;
            transition-duration: 300ms;
            transition-timing-function: ease-out;

            &.select-box-show {
                transform: translateZ(0);
            }

            .header {
                display: flex;
                box-sizing: border-box;
                width: 100%;
                justify-content: space-between;
                border-bottom: 1px solid $uni-border-color;
                line-height: 8rem;
                font-size: 2rem;
                padding: 0 $paddingLR;

                .cancel {
                    color: $uni-text-color-grey;
                }

                .all {
                    color: $uni-color-primary;

                    .all-active {
                        &::after {
                            display: inline-block;
                            content: "✔";
                            padding-left: 8rpx;
                        }
                    }
                }

                .confirm {
                    color: $uni-color-primary;
                }
            }

            .body-warp {
                width: 100%;
                height: 30vh;
                box-sizing: border-box;
                padding: 20rpx $paddingLR;
            }

            .body {
                width: 100%;
                height: 100%;
                overflow-y: auto;

                .empty-tips {
                    text-align: center;
                    font-size: 2rem;
                }

                .select-item {
                    display: flex;
                    font-size: 2rem;
                    line-height: 8rem;
                    color: #303133;
                    position: relative;
                    transition: all 0.3s;

                    &.selected {
                        color: $uni-color-primary;
                    }

                    &.disabled {
                        color: $uni-text-color-disable;
                    }

                    >.label {
                        flex: 1;
                        text-align: center;
                    }

                    >.selected-icon {
                        position: absolute;
                        right: 0;
                        top: 50%;
                        transform: translateY(-50%);
                    }
                }
            }
        }
    }
</style>